package com.edev.support.ddd;

import com.edev.support.dao.BasicDao;
import com.edev.support.dao.QueryDao;
import com.edev.support.ddd.join.JoinHelper;
import com.edev.support.ddd.join.RefHelper;
import com.edev.support.dsl.DomainObject;
import com.edev.support.dsl.DomainObjectFactory;
import com.edev.support.entity.Entity;
import com.edev.support.entity.ResultSet;
import com.edev.support.query.impl.QueryServiceImpl;
import com.edev.support.utils.SpringHelper;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.util.*;

/**
 * 对查询结果自动填充关联数据的查询服务实现类
 * <p>
 * 核心功能：
 * 1. 扩展基础查询服务，增加关联数据自动填充能力
 * 2. 支持JOIN关联（本地加载）和REF引用（远程加载）两种数据加载方式
 * 3. 批量处理查询结果集的关联数据加载（优化性能）
 */
@Slf4j
public class AutofillQueryServiceImpl extends QueryServiceImpl {
    @Autowired
    private SpringHelper springHelper;
    @Autowired
    private DddFactory dddFactory;
    private final BasicDao autofillDao;
    public AutofillQueryServiceImpl(@NonNull QueryDao queryDao, @NonNull BasicDao autofillDao) {
        super(queryDao);
        this.autofillDao = autofillDao;
    }

    /**
     * 查询后的数据处理（自动填充入口）
     * <p>
     * 执行流程：
     * 1. 调用父类基础查询处理
     * 2. 按实体类型分组结果集
     * 3. 批量加载各类关联数据
     * @param params 查询参数
     * @param resultSet 原始结果集
     * @return 包含关联数据的完整结果集
     */
    @Override
    protected ResultSet afterQuery(Map<String, Object> params, @NonNull ResultSet resultSet) {
        //if no result, do nothing, else fill data.
        Collection<?> data = resultSet.getData();
        if(data!=null&&!data.isEmpty()) {
            Map<Class<Entity<Serializable>>, List<Entity<Serializable>>> groupMap = groutByClass(data);
            groupMap.forEach(this::fillData);
        }
        return super.afterQuery(params, resultSet);
    }

    /**
     * 按照类型，对实体结果集进行分组
     * <p>
     * 处理逻辑：
     * 1. 验证结果集元素均为领域实体
     * 2. 按具体实体类型分组
     * @param collection 原始结果集
     * @return 按实体类分组的映射表（Key：实体类对象，Value：同类实体集合）
     */
    @SuppressWarnings("unchecked")
    protected Map<Class<Entity<Serializable>>, List<Entity<Serializable>>>
            groutByClass(@NonNull Collection<?> collection) {
        Map<Class<Entity<Serializable>>, List<Entity<Serializable>>> map = new HashMap<>();
        collection.forEach(row -> {
            if(!dddFactory.isEntity(row.getClass()))
                throw new DddException("cannot complete fill because the result set are not entities!");
            Entity<Serializable> entity = (Entity<Serializable>) row;
            List<Entity<Serializable>> list = map.computeIfAbsent((Class<Entity<Serializable>>) entity.getClass(), k -> new ArrayList<>());
            list.add(entity);
        });
        return map;
    }

    /**
     * 对关联数据进行填充
     * <p>
     * 处理流程：
     * 1. 获取领域对象元数据
     * 2. 遍历处理所有JOIN配置
     * 3. 遍历处理所有REF配置
     * @param clazz 当前处理的实体类
     * @param entities 同类实体集合
     */
    protected void fillData(Class<Entity<Serializable>> clazz, List<Entity<Serializable>> entities) {
        DomainObject dObj = DomainObjectFactory.getDomainObject(clazz);
        if(dObj.getJoins()!=null)
            dObj.getJoins().forEach(join -> autofillJoin(entities));
        if(dObj.getRefs()!=null)
            dObj.getRefs().forEach(ref -> autofillRef(entities));
    }

    /**
     * JOIN关联数据自动填充
     * @param list 需要填充的实体集合
     */
    private <E extends Entity<S>, S extends Serializable> void autofillJoin(Collection<E> list) {
        (new JoinHelper<E,S>(autofillDao)).setJoinForList(list);
    }

    /**
     * REF引用数据自动填充
     * @param list 需要填充的实体集合
     */
    private <E extends Entity<S>, S extends Serializable> void autofillRef(Collection<E> list) {
        (new RefHelper<E,S>(springHelper)).setRefForList(list);
    }
}
